﻿namespace Bridge.Base.Utilities
{
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Text.RegularExpressions;

    /// <summary>
    /// String extensions.
    /// </summary>
    public static class StringExtensions
    {
        /// <summary>
        /// Finds the first match matching the regex.
        /// </summary>
        /// <param name="self">String to search.</param>
        /// <param name="regexExpression">Regular Expression to use to search the string.</param>
        /// <returns>First match found by the Regex engine, null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if regexExpression is null.</exception>
        /// <exception cref="ArgumentException">Thrown if regexExpression or self is empty.</exception>
        public static string FindFirstMatch(this string self, string regexExpression)
        {
            self.ThrowIfNullOrEmpty("self");
            regexExpression.ThrowIfNullOrEmpty("regexExpression");

            return FindFirstMatch(self, regexExpression, NormalRegexOptions);
        }

        /// <summary>
        /// Finds the first match matching the regex.
        /// </summary>
        /// <param name="self">String to search.</param>
        /// <param name="regexExpression">Regular Expression to use to search the string.</param>
        /// <param name="regexOptions">RegexOptions to use to search the string.</param>
        /// <returns>First match found by the Regex engine, null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if regexExpression is null.</exception>
        /// <exception cref="ArgumentException">Thrown if regexExpression or self is empty.</exception>
        public static string FindFirstMatch(this string self, string regexExpression, RegexOptions regexOptions)
        {
            self.ThrowIfNullOrEmpty("self");
            regexExpression.ThrowIfNullOrEmpty("regexExpression");

            if (Regex.IsMatch(self, regexExpression, regexOptions))
            {
                MatchCollection matchCollection = Regex.Matches(self, regexExpression, regexOptions);
                return matchCollection[0].Value;
            }
            return null;
        }

        /// <summary>
        /// Finds all matches matching the regex.
        /// </summary>
        /// <param name="self">String to search.</param>
        /// <param name="regexExpression">Regular Expression to use to search the string.</param>
        /// <returns>All matches found by the Regex engine, null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if regexExpression is null.</exception>
        /// <exception cref="ArgumentException">Thrown if regexExpression or self is empty.</exception>
        public static StringCollection FindAllMatches(this string self, string regexExpression)
        {
            self.ThrowIfNullOrEmpty("self");
            regexExpression.ThrowIfNullOrEmpty("regexExpression");

            return FindAllMatches(self, regexExpression, NormalRegexOptions);
        }

        /// <summary>
        /// Finds all matches matching the regex.
        /// </summary>
        /// <param name="self">String to search.</param>
        /// <param name="regexExpression">Regular Expression to use to search the string.</param>
        /// <param name="regexOptions">RegexOptions to use to search the string.</param>
        /// <returns>All matches found by the Regex engine, null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if regexExpression is null.</exception>
        /// <exception cref="ArgumentException">Thrown if regexExpression or self is empty.</exception>
        public static StringCollection FindAllMatches(this string self, string regexExpression, RegexOptions regexOptions)
        {
            self.ThrowIfNullOrEmpty("self");
            regexExpression.ThrowIfNullOrEmpty("regexExpression");

            if (Regex.IsMatch(self, regexExpression, regexOptions))
            {
                StringCollection collection = new StringCollection();
                MatchCollection matchCollection = Regex.Matches(self, regexExpression, regexOptions);
                foreach (Match match in matchCollection)
                {
                    collection.Add(match.Value);
                }
                return collection;
            }
            return null;
        }

        /// <summary>
        /// Finds the last match matching the regex.
        /// </summary>
        /// <param name="self">String to search.</param>
        /// <param name="regexExpression">Regular Expression to use to search the string.</param>
        /// <returns>First match found by the Regex engine, null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if regexExpression is null.</exception>
        /// <exception cref="ArgumentException">Thrown if regexExpression or self is empty.</exception>
        public static string FindLastMatch(this string self, string regexExpression)
        {
            self.ThrowIfNullOrEmpty("self");
            regexExpression.ThrowIfNullOrEmpty("regexExpression");

            return FindLastMatch(self, regexExpression, NormalRegexOptions);
        }

        /// <summary>
        /// Finds the last match matching the regex.
        /// </summary>
        /// <param name="self">String to search.</param>
        /// <param name="regexExpression">Regular Expression to use to search the string.</param>
        /// <param name="regexOptions">RegexOptions to use to search the string.</param>
        /// <returns>First match found by the Regex engine, null if nothing is found.</returns>
        /// <exception cref="ArgumentNullException">Thrown if regexExpression is null.</exception>
        /// <exception cref="ArgumentException">Thrown if regexExpression or self is empty.</exception>
        public static string FindLastMatch(this string self, string regexExpression, RegexOptions regexOptions)
        {
            self.ThrowIfNullOrEmpty("self");
            regexExpression.ThrowIfNullOrEmpty("regexExpression");

            if (Regex.IsMatch(self, regexExpression, regexOptions))
            {
                MatchCollection matchCollection = Regex.Matches(self, regexExpression, regexOptions);
                return matchCollection[matchCollection.Count - 1].Value;
            }
            return null;
        }

        /// <summary>
        /// Throws appropriate exceptions if a string is null or empty.
        /// </summary>
        /// <param name="self">The string to check.</param>
        /// <param name="name">Parameter name to throw.</param>
        public static void ThrowIfNullOrEmpty(this string self, string paramName)
        {
            if (self == null) throw new ArgumentNullException(paramName);
            if (self.Length == 0) throw new ArgumentException(string.Format("{0} cannot be empty.", paramName), paramName);
        }

        /// <summary>
        /// Tries to get the Enum value of 
        /// </summary>
        /// <typeparam name="T">Type enum to find.</typeparam>
        /// <param name="self">String to search.</param>
        /// <param name="value">Value to send out.</param>
        /// <returns>True if a value was found; otherwise, false.</returns>
        /// <exception cref="ArgumentNullException">Thrown if self is null.</exception>
        /// <exception cref="ArgumentException">Thrown if self is empty.</exception>
        public static bool TryGetEnumValue<T>(this string self, out T value)
        {
            if (string.IsNullOrEmpty(self))
            {
                value = default(T);
                return false;
            }

            List<T> possibleEnums = new List<T>((T[])Enum.GetValues(typeof(T)));

            if (possibleEnums.Exists(match => match.ToString().Equals(self, StringComparison.OrdinalIgnoreCase)))
            {
                value = possibleEnums.Find(match => match.ToString().Equals(self, StringComparison.OrdinalIgnoreCase));
                return true;
            }
            value = default(T);
            return false;
        }

        private const RegexOptions NormalRegexOptions = RegexOptions.Singleline | RegexOptions.IgnoreCase;
    }
}
